/*
    Copyright 2006-2011 Patrik Jonsson, sunrise@familjenjonsson.org

    This file is part of Sunrise.

    Sunrise is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Sunrise is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Sunrise.  If not, see <http://www.gnu.org/licenses/>.

*/

/// \file
/// Simple wrappers for whatever random number generator is used.

// $Id$

#ifndef __random__
#define __random__

#include "random/uniform.h"
#include "mcrx-types.h"
#include "boost/thread/mutex.hpp"
#include "boost/shared_ptr.hpp"
#include <vector>

namespace mcrx {
  class mutex_random;
  /** Typedef for a generator that keeps INDEPENDENT state, not shared
      with any other generator.  (The mcrx::mutex_random class
      generator has shared state.) */
  typedef ranlib::Uniform<T_float, ranlib::MersenneTwister, 
    ranlib::independentState> T_rng;
  class global_random;
  class local_random;
}

/** A random number generator protected by a mutex.  All instances of
    this class share the random number state, so effectively they are
    all the same. Only one instance is used, mcrx::mcrx_random. */
class mcrx::mutex_random {
private:
  /// The Blitz random number generator.
  ranlib::Uniform<T_float> generator;
  /** The mutex used.  This is a static since the random numbers
      status is shared. */
  static boost::mutex mutex;
public:
  T_float random ();
  // Method to get N random numbers locking the mutex only once 
  template<int N> void random (blitz::TinyVector< T_float, N>&); 
  void seed (unsigned int s);
};

namespace mcrx {
  extern mutex_random mcrx_random;
  T_float rnd ();
  template<int N> void rnd (blitz::TinyVector< T_float, N>& r) {
    mcrx_random.random(r);}
  void seed (unsigned int s);
  std::vector<T_rng::T_state> generate_random_states (int n);
}

/** Fills the TinyVector with random numbers, locking the mutex only
    once.  This is more efficient than getting single numbers
    repeatedly. */
template<int N>
void mcrx::mutex_random::random (blitz::TinyVector<mcrx::T_float, N>& r)
{
  boost::mutex::scoped_lock lock (mutex);
  for (int i = 0; i < N; ++i)
    r [i] = generator.random();
};

/** Policy class for using the global, mutex-locked random number
    generator.  */
class mcrx::global_random {
public:
  /// Return a random number.
  T_float rnd () const { return mcrx::rnd ();};
  /// Fill a TinyVector with random numbers.
  template<int N> void rnd (blitz::TinyVector< T_float, N>& r) {
    mcrx_random.random(r);};
};

/** Policy class for using a local, non-locked random number
    generator. The copy constructor gives an object which shares the
    same random number generator as the argument.  The method
    make_unique () ensures that the random number generator is unique
    to this object. Note that to really get uncorrelated parallel
    random number sequences, you should also call make_unique() to set
    the Mersenne Twister parameters to one of the 48 unique parameter
    sets included in blitz.  */
class mcrx::local_random {
private:
  boost::shared_ptr<T_rng> rng_;

public:
  /// Default constructor creates its own generator.
  local_random() : rng_(new T_rng) {};

  /** Constructor creates a generator with specific ID for parallel
      applications. */
  local_random(unsigned int i) : rng_(new T_rng(i)) {};

  /// copy constructor copies the reference to the generator.
  local_random (const local_random& rhs) : rng_(rhs.rng_) {};
  
  /// Return a random number.
  T_float rnd () const {assert (rng_); return rng_->random ();};
  /// Fill a TinyVector with random numbers.
  template<int N> void rnd (blitz::TinyVector< T_float, N>& r) {
    assert (rng_);
    for (int i = 0; i < N; ++i)
      r [i] = rng_->random();
  };

  /// Make sure that the random number generator is not shared.
  void make_unique() {rng_.reset(new T_rng);};

  /** Make sure that the random number generator is not shared and has
      the specified ID number. This selects a set of unique Mersenne
      Twister parameters that give uncorrelated random
      sequences. There are currently 48 such sets included in blitz,
      so if we ever run with >48 threads we run a (small) risk of
      having correlated sequences. */
  void make_unique(unsigned int i) {rng_.reset(new T_rng(i));};

  /// Returns a reference to the random number generator.
  T_rng& get_generator () const {return *rng_;};
};

#endif

